﻿//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Microsoft Public License.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace DataServiceProvider
{
    using System;
    using System.Collections.Generic;
    using System.Data.Services.Providers;
    using System.Linq;
    using System.Collections;
    using System.Linq.Expressions;
    using System.Reflection;

    internal class DSPResourceQueryProviderModelExtension : IDataServiceQueryProvider
    {
        private DSPMetadataModelExtension metadata;
        private IDataServiceQueryProvider underlyingQueryProvider;

        public DSPResourceQueryProviderModelExtension(DSPMetadataModelExtension metadata, IDataServiceQueryProvider underlyingQueryProvider)
        {
            this.metadata = metadata;
            this.underlyingQueryProvider = underlyingQueryProvider;
        }


        #region IDataServiceQueryProvider Members

        public object CurrentDataSource
        {
            get
            {
                return this.underlyingQueryProvider.CurrentDataSource;
            }
            set
            {
                this.underlyingQueryProvider.CurrentDataSource = value;
            }
        }

        public object GetOpenPropertyValue(object target, string propertyName)
        {
            throw new NotImplementedException();
        }

        public IEnumerable<KeyValuePair<string, object>> GetOpenPropertyValues(object target)
        {
            throw new NotImplementedException();
        }

        private object GetExtendedPropertyValue<T>(object keyValue, IQueryable targetQueryable, DSPMetadataModelExtension.ExtendedResourcePropertyAnnotation extension)
        {
            IQueryable<T> query = targetQueryable as IQueryable<T>;
            var rpa = extension.TargetProperty.CustomState as DSPMetadataModelExtension.ResourcePropertyAnnotation;
            ParameterExpression param = Expression.Parameter(typeof(T), "it");
            Expression getTargetPropertyValue;
            if (extension.TargetProperty.CanReflectOnInstanceTypeProperty)
            {
                getTargetPropertyValue = Expression.Property(param, extension.TargetProperty.Name);
            }
            else
            {
                getTargetPropertyValue = Expression.Convert(
                    Expression.Call(
                        typeof(DataServiceProviderMethods).GetMethod("GetValue"),
                        param,
                        Expression.Constant(rpa == null ? extension.TargetProperty : rpa.OldResourceProperty, typeof(ResourceProperty))),
                    extension.TargetProperty.ResourceType.InstanceType);
            }
            Expression comparison = Expression.Equal(
                getTargetPropertyValue,
                Expression.Convert(Expression.Constant(keyValue, typeof(object)), extension.TargetProperty.ResourceType.InstanceType));
            LambdaExpression lambda = Expression.Lambda(comparison, param);
            var q = query.Where((Expression<Func<T, bool>>)lambda);
            foreach (var i in q)
            {
                return i;
            }

            return null;
        }

        public object GetPropertyValue(object target, ResourceProperty resourceProperty)
        {
            var extension = resourceProperty.CustomState as DSPMetadataModelExtension.ExtendedResourcePropertyAnnotation;
            if (extension != null)
            {
                // Need to get the value of the source property
                object keyValue = this.GetPropertyValue(target, extension.SourceProperty);
                IQueryable targetResourceSet = this.GetQueryRootForResourceSet(extension.TargetResourceSet);
                MethodInfo m = typeof(DSPResourceQueryProviderModelExtension).GetMethod("GetExtendedPropertyValue", BindingFlags.Instance | BindingFlags.NonPublic);
                return m.MakeGenericMethod(extension.TargetResourceType.InstanceType).Invoke(this, new object[] { keyValue, targetResourceSet, extension});
            }

            var rpa = resourceProperty.CustomState as DSPMetadataModelExtension.ResourcePropertyAnnotation;
            return this.underlyingQueryProvider.GetPropertyValue(target, rpa == null ? resourceProperty : rpa.OldResourceProperty);
        }

        public IQueryable GetQueryRootForResourceSet(ResourceSet resourceSet)
        {
            var rsa = resourceSet.CustomState as DSPMetadataModelExtension.ResourceSetAnnotation;
            return DSPLinqExtensionQueryProvider.CreateQuery(this.underlyingQueryProvider.GetQueryRootForResourceSet(rsa.OldResourceSet), this);
        }

        public ResourceType GetResourceType(object target)
        {
            ResourceType rt = this.underlyingQueryProvider.GetResourceType(target);
            ResourceType rt2;
            this.metadata.TryResolveResourceType(rt.FullName, out rt2);
            return rt2;
        }

        public object InvokeServiceOperation(ServiceOperation serviceOperation, object[] parameters)
        {
            throw new NotImplementedException();
        }

        public bool IsNullPropagationRequired
        {
            get { return this.underlyingQueryProvider.IsNullPropagationRequired; }
        }

        #endregion
    }
}